-
Notifications
You must be signed in to change notification settings - Fork 301
Improvements for pagination, sorting, filtering, and exception h… #416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…andling * Pagination updates: * Improve existing documentation of pagination, mixins * Document LimitOffsetPagination * Describe how to override pagination class query parameter names. * Remove reference to PAGINATE_BY_PARAM which was deprecated in DRF 3.2. * Document SparseFieldsetsMixin * Add new default settings for pagination query parameters and maximum size. * Add combinable mixins for filter and sort query parameters and make MultiplIDMixin combinable. * Exceptions updates: * Document JSON_API_UNIFORM_EXCEPTIONS setting. * handle missing fields exception thrown by new filter and sort Mixins as a 400 error. * Catch all exceptions not caught by DRF and format as JSON API error objects instead of returning HTML error pages.
Still TODO: write test cases for the new code, but I wanted to get a chance for others to take a look and provide feedback. |
Codecov Report
@@ Coverage Diff @@
## master #416 +/- ##
==========================================
+ Coverage 91.75% 91.96% +0.21%
==========================================
Files 55 55
Lines 2923 3026 +103
==========================================
+ Hits 2682 2783 +101
- Misses 241 243 +2
Continue to review full report at Codecov.
|
@mblayman Should I be adding stuff to the tox.ini so it matches whats in .travis.yml. flake8 and coverage are missing from it. |
I think I've got enough test cases. @mblayman: ready to merge if you see fit. |
@mblayman nagging about this PR. |
@sliverc perhaps you can review this PR? |
@n2ygk Thanks a lot for this contribution. I really like that this improves conformance with jsonapi specification! Certainly the way forward. |
@sliverc I could perhaps separate pagination from the others, but the Mixins and exception handler are linked, as the filter and sort mixins throw an exception if a query parameter contains an incorrect field name. I could also make the commit description shorter;-) If I were to split it into 4, it would I guess have to be in this order: 1) exception handler, 2) sort, 3) filter, 4) pagination. And then I'd have to split up the test case and documentation changes into 4 pieces as well. That's a lot of work. Maybe you can let me slide this time? |
Let me add some general comments before we decide how or whether we want to split this PR up.
|
@sliverc I'm not familiar with how the DjangoFilterBackends are configured. I'll have to read up on that. I based my stuff on the MultipleIDMixin example. |
I haven't noticed that there is a I think though we should rather deprecate it, as firstly I do not think the json api specification mentions that this is possible at all and I also don't think it is the right way to implement this as DRF has better options to do so (e.g. filter backends). Question is how we want to move forward with this PR. As it implements quite a few things which we might need to consider how they are best implemented I would suggest we close this PR and we can open new PRs for each area of problem? (Not sure when I will get around but a few problems you are trying to address I certainly also encountered in the past and would like to see fixed) What do you think? Or do you have any other suggestions? |
@sliverc wrote:
MultipleIDMixin was added by @gaker in 2014. The jsonapi spec wasn't finalized until 2015 so things may have changed. Certainly the way to do this per jsonapi 1.0 would be to use
I can close this PR and split it up into exceptions, pagination improvements and mixins (or filter backends if that make more sense -- I will have to research that.) |
@sliverc I finally think I might have a decent way to customize from django_filters.rest_framework import DjangoFilterBackend
class JsonApiFilterBackend(DjangoFilterBackend):
"""
Override's django_filters.rest_framework.DjangoFilterBackend to use `filter[field]` query parameter.
"""
def get_filterset_kwargs(self, request, queryset, view):
"""
Turns filter[<field>]=<value> into <field>=<value> which is what DjangoFilterBackend expects
N.B. `filter[all]` is special-cased for multi-field keyword searches done by rest_framework.filters.SearchFilter
TODO: use SearchFilter's `search_param` instead of hardcoding
"""
data = request.query_params.copy()
for qp, val in data.items():
if qp[:7] == 'filter[' and qp[-1] == ']' and qp != 'filter[all]':
data[qp[7:-1]] = val
del data[qp]
return {
'data': data,
'queryset': queryset,
'request': request,
} I don't know if the upstream project (django-filters) would accept a request to make the query parameters configurable in this way... My settings snippet looks like this: 'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.OrderingFilter', # for sort
'myapp.backends.JsonApiFilterBackend', # for `filter[field]` filtering
'rest_framework.filters.SearchFilter', # for keyword filtering across multiple fields
),
'ORDERING_PARAM': 'sort',
'SEARCH_PARAM': 'filter[all]', And viewset looks like this: class CourseViewSet(CourseBaseViewSet):
queryset = Course.objects.all()
serializer_class = CourseSerializer
filterset_fields = ('subject_area_code', 'course_name', 'course_description',)
search_fields = ('course_name', 'course_description', 'course_identifier', 'course_number',) |
@n2ygk As I think what such a filter backend should also do is formatting taking Feel free to open up with a PR with this. |
@sliverc I'll start a PR shortly. Just a few items to get your feedback:
|
Thanks for summarizing. The points I ticked above look good to me. Concerning the others points some thoughts: This means we should not worry about how filter are named, whether __ or gt are allowed etc. What we should worry about is that the right query param name is used ( django-filter has its own way of defining filters which is fairly static (no dynamic filters). Maybe we also add a glue to https://github.com/philipn/django-rest-framework-filters which is more dynamic for users which prefer Django ORM specific filters. What do you think? To keep it simple and get started I would create a DJA version of Django Filter backend and once this is integrated can be expanded. |
@sliverc You don't like rewriting |
I think in a first implementation I would certainly leave it out and keep it simple. Once it is established we can still think about improving it (although the specification is silent on this - it only talks about member names but filter is not a member). |
Actually a filter is a member; that’s what goes inside the brackets per the
examples. At least that’s my reading of it.
…On Tue, Aug 7, 2018 at 3:03 AM Oliver Sauder ***@***.***> wrote:
I think in a first implementation I would certainly leave it out and keep
it simple. Once it is established we can still think about improving it
(although the specification is silent on this - it only talks about member
names but filter is not a member).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#416 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AEJ5d5OFCwlf3Z6V9ac2bwRU3OvjV9Y-ks5uOTujgaJpZM4S2L4M>
.
|
Interesting I understand the specification completely different 😄
According to this is anything is allowed as filter as long as it uses the filter query param e.g. also |
@sliverc is there an inverse function for |
Copied from JSONParser where it basicially needs to do the same thing: # convert back to python/rest_framework's preferred underscore format
return utils._format_object(attributes, 'underscore') |
…andling